//: src/com/silvtar/net/ftp/SSFtpClient.java
/* 
 * Created by 朴晟宁 <piaosn@gmail.com> on 4/21/2013. 
 * Copyright (c) 2013 朴晟宁. All rights reserved.
 * 
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program;
 * 
 * *********************************************************************
 * 
 * Modify History:
 * Date        Version    Description
 * 5/1/2013    0.1        Create the class.
 * 5/2/2013    0.2        It become easy to use now.
 */

package com.silvtar.net.ftp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import com.silvtar.io.StdOut;
import com.silvtar.io.StreamToolkit;

/**
 * FTP客户端类 本类把一些常用的FTP操作进行了封装。使FTP较为易用，直观。
 * 
 * @author silvtar
 * @author piaosn@gmail.com
 * @version 0.2
 */
public class SSFtpClient {

    private FTPClient ftpClient;

    private String    ip       = "127.0.0.1";
    private int       port     = 21;

    private String    username = "silvtar";
    private String    password = "xinling020s";

    /**
     * 初始化FTP客户端 在初始化后还需要调用connect()进行连接。然后调用login登陆.
     */
    public SSFtpClient() {

        FTPClientConfig config = new FTPClientConfig();
        config.setServerLanguageCode("utf-8");

        ftpClient = new FTPClient();
        ftpClient.setControlEncoding("utf-8");

        ftpClient.configure(config);

    }

    /**
     * 配置函数，传输前需调用本函数配置FTP客户端
     * 
     * @param ipAddr
     *            FTP服务器的IP地址
     * @param port
     *            FTP服务的端口号
     * @param username
     *            用户名
     * @param password
     *            密码
     */
    public void config(String ipAddr, int port, String username, String password) {
        this.ip = ipAddr;
        this.port = port;
        this.username = username;
        this.password = password;
    }

    // ============================== 初始化 ================================
    /**
     * 以预设的IP和端口号连接FTP服务器，连接后如想要操作还需进行login()。
     */
    public void connect() {
        try {
            int reply;
            ftpClient.connect(ip, port);
            StdOut.println("FTPClient: Connected to FTP Server: " + ip);
            StdOut.println(ftpClient.getReplyString());

            reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftpClient.disconnect();
                StdOut.printErr("FTPClient: FTP server refused connection.");
            }

        } catch (IOException e) {
            StdOut.println("FTPClient: Connect to server:" + ip + ":" + port
                    + " false.");
            e.printStackTrace();
        }
    }

    /**
     * 以预设的用户名和密码登陆FTP服务器。登陆后会设置传输模式为二进制。
     */
    public void login() {
        try {
            if (!ftpClient.login(username, password)) {
                StdOut.printErr("login false!");
            } else {
                ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            }
        } catch (IOException e) {
            StdOut.println("FTPClient: Logining to server:" + ip + ":" + port
                    + ", username = " + username + " false.");
            e.printStackTrace();
        }

    }

    /**
     * 传输完毕后断开连接
     */
    public void disconnect() {
        if (ftpClient.isConnected()) {
            try {
                ftpClient.disconnect();
            } catch (IOException ioe) {
                StdOut.println("FTPClient: Disconnect from server:" + ip + ":"
                        + port + " false.");
            }
        }
    }

    // ============================== 常用命令 ================================
    /**
     * 列出pathName指定的文件夹内容
     * 
     * @param pathName
     *            文件夹路径
     * @return 文件列表
     */
    public FTPFile[] ls(String pathName) {
        FTPFile[] files = null;
        try {
            files = ftpClient.listFiles(pathName);
        } catch (IOException e) {
            StdOut.printErr("FTPClient: ls: " + pathName + "false.");
            e.printStackTrace();
        }
        for (int i = 0; i < files.length; i++) {
            System.out.println(files[i].getName());
        }
        return files;
    }

    /**
     * 列出当前内容
     * 
     * @return 文件列表
     */
    public FTPFile[] ls() {
        return ls("");
    }

    /**
     * 改变当前目录
     * 
     * @param pathName
     *            路径
     * @return 是否成功
     */
    public boolean cd(String pathName) {

        try {
            ftpClient.changeWorkingDirectory(pathName);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 新建文件夹
     * 
     * @param pathName
     *            文件夹名称，或路径名
     * @return 是否成功
     */
    public boolean mkdir(String pathName) {
        boolean isSuccess = false;
        try {
            isSuccess = ftpClient.makeDirectory(pathName);
        } catch (IOException e) {
            StdOut.printErr("FTPClient: mkdir: " + pathName + "false.");
            e.printStackTrace();
        }
        return isSuccess;
    }

    public boolean rm(String pathName) {
        boolean isSuccess = false;
        try {
            isSuccess = ftpClient.deleteFile(pathName);
        } catch (IOException e) {
            StdOut.printErr("FTPClient: rm: " + pathName + "false.");
            e.printStackTrace();
        }
        return isSuccess;
    }

    public boolean rmdir(String pathname) {
        boolean isSuccess = false;
        try {
            isSuccess = ftpClient.removeDirectory(pathname);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return isSuccess;
    }

    public boolean rmdir(String path, boolean isAll) {
        boolean isSuccess = false;
        if (!isAll) {
            try {
                isSuccess = ftpClient.removeDirectory(path);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        FTPFile[] ftpFileArr = null;
        try {
            ftpFileArr = ftpClient.listFiles(path);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if (ftpFileArr == null || ftpFileArr.length == 0) {
            try {
                isSuccess = ftpClient.removeDirectory(path);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        //
        for (FTPFile ftpFile : ftpFileArr) {
            String name = ftpFile.getName();
            if (ftpFile.isDirectory()) {
                System.out.println("* [sD]Delete subPath [" + path + "/" + name
                        + "]");
                rmdir(path + "/" + name, true);
            } else if (ftpFile.isFile()) {
                System.out.println("* [sF]Delete file [" + path + "/" + name
                        + "]");
                rm(path + "/" + name);
            } else if (ftpFile.isSymbolicLink()) {

            } else if (ftpFile.isUnknown()) {

            }
        }
        try {
            isSuccess = ftpClient.removeDirectory(path);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return isSuccess;
    }

    public boolean exist(String pathName) {
        boolean isExist = false;
        FTPFile[] ftpFileArr = ls(pathName);

        for (FTPFile ftpFile : ftpFileArr) {
            if (ftpFile.getName().equals(pathName)) {

                isExist = true;
                break;
            }
        }
        return isExist;
    }

    // ============================== 下载与上传 ================================

    /**
     * 上传文件 这个方法会将localInputStream传输到FTP服务器中
     * 
     * @param remoteFileName
     *            服务器上的文件名/路径
     * @param localFileName
     *            本地文件的文件名/路径
     * @return 是否成功传输
     */
    public boolean upload(String remoteFileName, String localFileName) {
        InputStream localInputStream = new StreamToolkit(localFileName)
                .getInputStream();
        return upload(remoteFileName, localInputStream);
    }

    /**
     * 上传文件 这个方法会将localInputStream传输到FTP服务器中
     * 
     * @param remoteFileName
     *            服务器上的文件名/路径
     * @param localInputStream
     *            本地文件输入流
     * @return 是否成功传输
     */
    public boolean upload(String remoteFileName, InputStream localInputStream) {
        boolean flag = false;
        try {
            flag = ftpClient.storeFile(remoteFileName, localInputStream);
        } catch (IOException e) {
            flag = false;
            return flag;
        } finally {
            if (localInputStream != null) {
                try {
                    localInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return flag;
    }

    /**
     * 获取上传文件流 这个方法会返回服务器文件的OutputStream流。
     * 
     * @param remoteFileName
     *            服务器上的文件名/路径
     * @return 文件输出流
     */
    public OutputStream uploadStream(String remoteFileName) {
        OutputStream outputStream = null;
        try {
            outputStream = ftpClient.storeFileStream(remoteFileName);
        } catch (IOException e) {
            StdOut.printErr("FTPClient: get output stream: " + remoteFileName
                    + "false.");
            e.printStackTrace();
        }
        return outputStream;
    }

    /**
     * 下载文件 这个方法会将文件传输到localOutputStream中
     * 
     * @param remoteFileName
     *            服务器上的文件名/路径
     * @param localFileName
     *            本地文件的文件名/路径
     * @return 是否传输成功
     */
    public boolean download(String remoteFileName, String localFileName) {
        OutputStream localOutputStream = new StreamToolkit(localFileName, "rw")
                .getOutputStream();
        return download(remoteFileName, localOutputStream);
    }

    /**
     * 下载文件 这个方法会将文件传输到localOutputStream中
     * 
     * @param remoteFileName
     *            服务器上的文件名/路径
     * @param localOutputStream
     *            本地输出流
     * @return 是否传输成功
     */
    public boolean download(String remoteFileName,
            OutputStream localOutputStream) {
        boolean flag = false;
        try {
            flag = ftpClient.retrieveFile(remoteFileName, localOutputStream);
        } catch (IOException e) {
            flag = false;
            return flag;
        } finally {
            try {
                localOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return flag;
    }

    /**
     * 获得下载文件流 这个方法会返回服务器文件的InputStream流。
     * 
     * @param remoteFileName
     *            服务器上的文件名/路径
     * @return 文件输入流
     */
    public InputStream downloadStream(String remoteFileName) {
        InputStream inputStream = null;
        try {
            inputStream = ftpClient.retrieveFileStream(remoteFileName);
        } catch (IOException e) {
            StdOut.printErr("FTPClient: get input stream: " + remoteFileName
                    + "false.");
            e.printStackTrace();
        }
        return inputStream;
    }
}
